<template>
    <div class="search-tree">
        <div class="search">
            <a-input
                placeholder="请输入"
                allow-clear
                v-model="searchKey"
                @change="handleSearch"
            />
            <br />
        </div>
        <!-- 虚拟滚动容器 -->
        <div class="virtual-tree" ref="container" @scroll="handleScroll">
            <!-- 虚拟滚动占位元素，高度等于所有节点总高度 -->
            <div class="virtual-scroll" :style="{ height: totalHeight + 'px' }">
                <!-- 实际渲染的节点列表，通过transform控制显示位置 -->
                <div
                    class="node-list"
                    :style="{ transform: `translateY(${renderOffset}px)` }"
                >
                    <!-- 只渲染可见区域的节点 -->
                    <div
                        v-for="node in visibleNodes"
                        :key="node.id"
                        class="tree-node"
                        :style="getNodeStyle(node)"
                    >
                        <!-- 展开/折叠图标（仅对有子节点的节点显示） -->
                        <span
                            v-if="hasChildren(node)"
                            class="expand-icon"
                            :class="{ 'is-expanded': node.expanded }"
                            @click="onExpand(node)"
                        >
                            <a-icon type="right" class="icon-arrow" />
                        </span>
                        <span v-else class="icon-placeholder"></span>

                        <!-- 复选框 -->
                        <label class="tree-checkbox">
                            <input
                                type="checkbox"
                                :checked="isChecked(node)"
                                :indeterminate="isHalfChecked(node)"
                                @change="onCheck(node, $event)"
                            />
                            <span class="checkbox-core"></span>
                        </label>

                        <!-- 节点图标和名称 -->
                        <a-icon type="user" v-if="node.nodeType === 13" />
                        <a-icon type="team" v-if="node.nodeType === 2" />
                        <span class="node-label">{{ node.name }}</span>
                    </div>
                </div>
            </div>
        </div>
    </div>
</template>

<script>
export default {
    name: "VirtualTree",
    props: {
        dataSource: {
            type: Array,
            required: true,
            default: () => [],
        },
        nodeHeight: {
            type: Number,
            default: 32,
        },
        indent: {
            type: Number,
            default: 20,
        },
        buffer: {
            type: Number,
            default: 25,
        },
        defaultNodes: {
            type: Array,
            default: () => [],
        },
    },
    data() {
        return {
            filteredData: [],
            searchKey: "",
            treeData: [],
            scrollTop: 0,
            lastScrollTime: 0,
            containerHeight: 0,
            expandedNodes: {},
            checkedStates: {},
            halfCheckedStates: {},
        };
    },
    computed: {
        flatNodes() {
            const nodes = this.searchKey ? this.filteredData : this.treeData;
            return this.flattenTree(nodes);
        },
        totalHeight() {
            return this.flatNodes.length * this.nodeHeight;
        },
        renderOffset() {
            return Math.max(
                0,
                Math.floor(this.scrollTop / this.nodeHeight) * this.nodeHeight -
                    this.buffer * this.nodeHeight
            );
        },
        visibleRange() {
            const start = Math.max(
                0,
                Math.floor(this.scrollTop / this.nodeHeight) - this.buffer
            );
            const end =
                start +
                Math.ceil(this.containerHeight / this.nodeHeight) +
                this.buffer * 2;
            return [start, Math.min(end, this.flatNodes.length)];
        },
        visibleNodes() {
            const [start, end] = this.visibleRange;
            return this.flatNodes.slice(start, end);
        },
    },
    watch: {
        dataSource: {
            immediate: true,
            deep: true,
            handler(newVal) {
                if (newVal?.length) {
                    this.treeData = this.deepClone(newVal);
                    this.initExpandedNodes();
                    this.initCheckedNodes();
                    this.$nextTick(() => {
                        this.updateContainerHeight();
                    });
                }
            },
        },
        defaultNodes: {
            immediate: true,
            deep: true,
            handler(newVal) {
                if (newVal?.length && this.dataSource?.length) {
                    this.initCheckedNodes();
                }
            },
        },
    },
    mounted() {
        this.updateContainerHeight();
        window.addEventListener("resize", this.updateContainerHeight);
    },
    beforeDestroy() {
        window.removeEventListener("resize", this.updateContainerHeight);
    },
    methods: {
        deepClone(obj) {
            return JSON.parse(JSON.stringify(obj));
        },

        initExpandedNodes() {
            if (this.treeData[0]?.id) {
                this.$set(
                    this.expandedNodes,
                    this.treeData[0].id,
                    this.treeData[0]
                );
            }
        },

        initCheckedNodes() {
            if (!this.defaultNodes?.length) return;

            // 先清空原有状态
            this.checkedStates = {};
            this.halfCheckedStates = {};

            // 设置新选中状态
            this.defaultNodes.forEach((nodeId) => {
                const node = this.findNodeById(this.treeData, nodeId);
                if (node) {
                    this.updateCheckedState(node, true);
                }
            });

            // 更新所有父节点状态
            this.updateAllParentStates();
        },

        findParentNode(node) {
            if (!node.parentId) return null;
            return this.findNodeById(this.treeData, node.parentId);
        },

        updateSingleParentState(parent) {
            const children = parent.children || [];
            const checkedChildren = children.filter(
                (child) => this.checkedStates[child.id]
            );
            const halfCheckedChildren = children.filter(
                (child) => this.halfCheckedStates[child.id]
            );

            if (checkedChildren.length === children.length) {
                // 全选
                this.$set(this.checkedStates, parent.id, parent);
                this.$delete(this.halfCheckedStates, parent.id);
            } else if (
                checkedChildren.length > 0 ||
                halfCheckedChildren.length > 0
            ) {
                // 半选
                this.$delete(this.checkedStates, parent.id);
                this.$set(this.halfCheckedStates, parent.id, parent);
            } else {
                // 未选
                this.$delete(this.checkedStates, parent.id);
                this.$delete(this.halfCheckedStates, parent.id);
            }
        },

        handleSearch() {
            if (!this.searchKey.trim()) {
                this.filteredData = [];
            } else {
                this.filteredData = this.filterTree(
                    this.treeData,
                    this.searchKey.toLowerCase()
                );
                this.expandMatchingNodes();
            }
            // 搜索条件变化时重新计算所有父节点状态
            this.updateAllParentStates();
        },

        filterTree(nodes, query) {
            return nodes.reduce((list, node) => {
                const isMatch = node.name.toLowerCase().includes(query);
                const newNode = { ...node, isMatch };

                if (this.checkedStates[node.id]) {
                    newNode.checked = true;
                }

                if (isMatch) {
                    list.push(newNode);
                    return list;
                }

                const children = node.children
                    ? this.filterTree(node.children, query)
                    : [];
                if (children.length) {
                    list.push({ ...newNode, children });
                }
                return list;
            }, []);
        },

        expandMatchingNodes() {
            const expandParents = (node) => {
                if (node.parentId) {
                    this.$set(this.expandedNodes, node.parentId, node);
                    const parent = this.findNodeById(
                        this.treeData,
                        node.parentId
                    );
                    if (parent) expandParents(parent);
                }
            };

            this.flatNodes.forEach((node) => {
                if (node.isMatch) {
                    this.$set(this.expandedNodes, node.id, node);
                    expandParents(node);
                }
            });
        },

        findNodeById(nodes, id) {
            for (const node of nodes) {
                if (node.id === id) return node;
                if (node.children) {
                    const found = this.findNodeById(node.children, id);
                    if (found) return found;
                }
            }
            return null;
        },

        updateContainerHeight() {
            this.containerHeight = this.$refs.container?.clientHeight || 0;
        },

        handleScroll() {
            const now = Date.now();
            if (now - this.lastScrollTime < 16) return;
            this.lastScrollTime = now;
            this.scrollTop = this.$refs.container?.scrollTop || 0;
        },

        getNodeStyle(node) {
            return {
                height: `${this.nodeHeight}px`,
                paddingLeft: `${node.level * this.indent + 8}px`,
            };
        },

        flattenTree(nodes, level = 0, parent = null) {
            return nodes.reduce((list, node) => {
                const isExpanded = !!this.expandedNodes[node.id];
                this.$set(node, "level", level);
                this.$set(node, "parentId", parent?.id || null);
                this.$set(node, "expanded", isExpanded);
                this.$set(node, "hasChildren", !!node.children?.length);
                list.push(node);

                if (isExpanded && node.children) {
                    list.push(
                        ...this.flattenTree(node.children, level + 1, node)
                    );
                }
                return list;
            }, []);
        },

        onExpand(node) {
            if (!node.expanded) {
                this.$set(this.expandedNodes, node.id, node);
            } else {
                this.$delete(this.expandedNodes, node.id);
            }
        },

        onCheck(node, event) {
            const checked = event.target.checked;
            this.updateCheckedState(node, checked);
            this.updateParentCheckedState(node);
        },

        updateCheckedState(node, checked) {
            if (checked) {
                this.$set(this.checkedStates, node.id, node);
                this.$delete(this.halfCheckedStates, node.id);
            } else {
                this.$delete(this.checkedStates, node.id);
                this.$delete(this.halfCheckedStates, node.id);
            }

            // 递归处理子节点
            if (node.children) {
                node.children.forEach((child) => {
                    this.updateCheckedState(child, checked);
                });
            }
        },

        updateParentCheckedState(node) {
            if (!node.parentId) return;

            const parent = this.findNodeById(this.treeData, node.parentId);
            if (!parent) return;

            this.updateSingleParentState(parent);
            
            // 递归向上更新
            this.updateParentCheckedState(parent);
        },

        updateAllParentStates() {
            // 获取所有需要更新的节点（包括已选中节点的所有父节点）
            const allNodes = this.flattenTree(this.treeData);
            const nodesToUpdate = new Set();
            
            // 收集所有需要更新的父节点
            allNodes.forEach(node => {
                if (node.children && node.children.length) {
                    nodesToUpdate.add(node);
                }
            });
            
            // 从叶子节点开始向上更新状态
            const sortedNodes = Array.from(nodesToUpdate).sort((a, b) => b.level - a.level);
            sortedNodes.forEach(node => {
                this.updateSingleParentState(node);
            });
        },

        hasChildren(node) {
            return node.children?.length > 0;
        },

        isChecked(node) {
            return this.checkedStates[node.id] ?? false;
        },

        isHalfChecked(node) {
            return this.halfCheckedStates[node.id] ?? false;
        },

        getCheckedNodes() {
            // 只返回人员节点(nodeType为13)
            return Object.values(this.checkedStates).filter(
                (node) => node.nodeType === 13
            );
        },
    },
};
</script>

<style scoped lang="less">
.search-tree {
    height: 100%;
    min-height: 400px;
    display: flex;
    flex-direction: column;

    .search {
        padding: 16px 16px 12px;
    }
    .virtual-tree {
        padding: 0 16px;
        height: 50%;
        flex: 1;
        overflow: auto;
        position: relative;

        .virtual-scroll {
            position: relative;
        }

        .node-list {
            position: absolute;
            width: 100%;
            will-change: transform;

            .tree-node {
                display: flex;
                align-items: center;
                position: relative;
                padding-right: 12px;
                margin-bottom: 4px;
                transition: background-color 0.2s;

                &:hover {
                    background-color: rgba(255, 44, 44, 0.08);
                }
                .expand-icon {
                    width: 24px;
                    height: 24px;
                    display: flex;
                    align-items: center;
                    justify-content: center;
                    cursor: pointer;
                    margin-right: 4px;

                    .icon-arrow {
                        font-size: 12px;
                        color: #c0c4cc;
                        transition: transform 0.2s;
                    }
                }
                .expand-icon.is-expanded .icon-arrow {
                    transform: rotate(90deg);
                }

                .icon-placeholder {
                    width: 24px;
                    margin-right: 4px;
                }

                .tree-checkbox {
                    position: relative;
                    margin-right: 6px;
                    display: flex;

                    input {
                        position: absolute;
                        opacity: 0;
                        width: 16px;
                        height: 16px;
                        z-index: 1;
                        cursor: pointer;

                        &[indeterminate] + .checkbox-core {
                            background-color: #eb4d44;
                            border-color: #eb4d44;

                            &::after {
                                content: "";
                                position: absolute;
                                left: 3px;
                                top: 6px;
                                width: 9px;
                                height: 2px;
                                background: #fff;
                            }
                        }

                        &:checked + .checkbox-core {
                            background-color: #eb4d44;
                            border-color: #eb4d44;

                            &::after {
                                content: "";
                                position: absolute;
                                left: 5px;
                                top: 1px;
                                width: 5px;
                                height: 8px;
                                border: 2px solid #fff;
                                border-left: 0;
                                border-top: 0;
                                transform: rotate(45deg);
                            }
                        }
                    }

                    .checkbox-core {
                        display: inline-block;
                        width: 16px;
                        height: 16px;
                        border: 1px solid #dcdfe6;
                        border-radius: 2px;
                        background-color: #fff;
                        position: relative;
                    }
                }

                .node-label {
                    flex: 1;
                    overflow: hidden;
                    text-overflow: ellipsis;
                    white-space: nowrap;
                    margin-left: 4px;
                }
            }
        }
    }
}
</style>